﻿package com.quickdev.intelligenthome.net
{
	import com.adobe.serialization.json.JSON;
	import com.quickdev.intelligenthome.config.Config;
	import com.quickdev.intelligenthome.net.data.Base;
	
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.utils.ByteArray;
	import com.quickdev.intelligenthome.utils.CRC32;
	import com.quickdev.intelligenthome.utils.CRC16;
	import com.quickdev.intelligenthome.utils.CRC;
	import flash.events.ProgressEvent;
	import flash.utils.Timer;
	import flash.events.TimerEvent;

	public class Service
	{
		private static var _inst :Service = null;
		
		private var socket :Socket = null;
		
		private var reconnectNum :uint = 3;
		private static const DURATION :uint = 5000;
		private var timer :Timer = new Timer( DURATION, 1 );
		
		private var toSendArr :Array = new Array();
		
		public function Service( s :Single)
		{
			socket = new Socket();
			addListener();
			
			trace("connect host:" + Config.serviceUrl + " port:" + Config.port);
			socket.connect( Config.serviceUrl, Config.port );
		}
		
		private function addListener() :void {
			socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
			socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSError);
			socket.addEventListener(Event.CLOSE, onClose);
			socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
			socket.addEventListener(Event.CONNECT, onConnect);
			
			timer.addEventListener(TimerEvent.TIMER, reConnect);
		}
		
		private function onConnect(event :Event) :void {
			var info :Object = toSendArr.shift();
			while( info ) {
				socket.writeBytes( info as ByteArray );
				socket.flush();
				
				info = toSendArr.shift();
			}
		}
		
		private function onIOError(event :IOErrorEvent) :void {
			if( reconnectNum > 0 ) {
				if( timer.running ) {
					timer.stop();
				}
				timer.start();
			}
		}
		
		
		private function onSError(event :SecurityErrorEvent) :void {
			if( reconnectNum > 0 ) {
				if( timer.running ) {
					timer.stop();
				}
				timer.start();
			}
		}
		
		private function onClose(event :Event) :void {
			socket.connect( Config.serviceUrl, Config.port );
			reconnectNum = 3;
			if( timer.running ) {
				timer.stop();
			}
			timer.start();
		}
		
		private function onData(event :ProgressEvent) :void {
			
		}
		
		private function reConnect(event :TimerEvent) :void {
			--reconnectNum;
			trace("reconnect host:" + Config.serviceUrl + " port:" + Config.port);
			socket.connect( Config.serviceUrl, Config.port );
		}
		
		public static function getInstance() :Service {
			if( _inst == null ) {
				_inst = new Service( new Single() );
			}
			
			return _inst;
		}
		
		private function writeHeader(byteArr :ByteArray) :void {
			byteArr.writeMultiByte("AA", "utf-8");
		}
		
		private function writeLength(byteArr :ByteArray, length :uint) :void {
			var str :String = "";
			var a :uint;
			var divided :uint = 1000;
			while( divided > 0 ) {
				a = length / divided;
				str += a;
				length %= divided;
				divided /= 10;
			}

			byteArr.writeMultiByte(str, "utf-8");
		}
		
		private function writeData(byteArr :ByteArray, data :String) :void {
			//data = "{\"msgType\":\"01\",\"userNo\":\"100000000842\",\"queryType\":\"0\",\"startDate\":\"2011-11-01\",\"endDate\":\"2011-12-31\"}";
			byteArr.writeMultiByte(data, "utf-8");
		}
		
		private function writeCRC(byteArr :ByteArray, data :String) :void {
			var ba :ByteArray = new ByteArray();
			//data = "{\"msgType\":\"01\",\"userNo\":\"100000000842\",\"queryType\":\"0\",\"startDate\":\"2011-11-01\",\"endDate\":\"2011-12-31\"}";
			ba.writeMultiByte(data, "utf-8");
			var crc :CRC = new CRC16();
			crc.update(ba);
			var str :String = crc.getValue().toString(16).toUpperCase();
			var length :uint = str.length;
			for(var i :uint = 0; i < 4 - length; ++i) {
				str = "0" + str;
			} 
			byteArr.writeMultiByte(str, "utf-8");
		}
		
		private function writeFooter(byteArr :ByteArray) :void {
			byteArr.writeMultiByte("BB", "utf-8");
		}
		
		public function query( request :Base, callBack :Function ) :void {
			var byteArr :ByteArray = new ByteArray();
			writeHeader( byteArr );
			
			var data :String = request.encode();
			//data = "{\"msgType\":\"01\",\"userNo\":\"100000000842\",\"queryType\":\"0\",\"startDate\":\"2011-11-01\",\"endDate\":\"2011-12-31\"}";
			var length :uint = data.length;
			
			writeLength(byteArr, length);
			writeData(byteArr, request.encode());
			writeCRC(byteArr, request.encode());
			writeFooter(byteArr);
			if( socket.connected ) {
				socket.writeBytes( byteArr );
				socket.flush();
			}
			else {
				toSendArr.push( byteArr );
				onClose(null);
			}
			/*
			var urlReq :URLRequest = new URLRequest( Config.serviceUrl );
			urlReq.method = URLRequestMethod.POST;
			var urlVar :URLVariables = new URLVariables();
			urlVar.data = JSON.stringify( request );
			urlReq.data = urlVar;
			
			var loader :CallBackLoader = new CallBackLoader();
			loader.callBack = callBack;
			loader.addEventListener(Event.COMPLETE, onQuery);
			loader.load( urlReq );
			*/
		}
		
		private function onQuery( event :Event ) :void {
			/*
			var loader :CallBackLoader = event.target as CallBackLoader;
			var obj :Object = JSON.parse( loader.data );
			if( loader.callBack ) {
				//loader.callBack( obj );
				loader.callBack( loader.data );
			}
			*/
		}
		
		
	}
}

class Single {}